/*
 * 描述： <描述>
 * 修改人： rain
 * 修改时间： 2016年8月9日
 * 项目： rainhy-config
 */
package com.rainhy.config.context;

import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.InitializingBean;

import com.rainhy.RainhyConstant;
import com.rainhy.config.config.ConfigGroup;
import com.rainhy.config.config.ConfigProperty;
import com.rainhy.core.exception.RainhyParseOrFormatException;
import com.rainhy.core.util.AssertUtils;
import com.rainhy.core.util.JsonUtils;
import com.rainhy.core.util.PatternUtils.RegExp;

/**
 * 配置容器
 * 
 * @author rain
 * @version [版本号, 2016年8月9日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class ConfigContext extends ConfigContextManagerBuilder implements InitializingBean {
    
    /** 自身唯一引用 */
    private static ConfigContext context;
    
    /** 返回自身唯一引用 */
    public static ConfigContext getContext() {
        return ConfigContext.context;
    }
    
    /**
     * 
     * 获取配置属性的实际值
     * 
     * @param key 键
     * 
     * @return String 值
     * @exception [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    public static String getValue(String key) {
        return getContext().getConfigPropertyValue(key);
    }
    
    /**
     * 
     * 获取配置属性的实际值<br>
     * key对应的值不存在,或者获取失败时,返回默认值
     * 
     * @param key 键
     * @param defaultString 默认值
     * 
     * @return String 值
     * @exception [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    public static String getValue(String key, String defaultString) {
        boolean contains = getContext().contains(key);
        if (contains) {
            try {
                return getValue(key);
            } catch (Exception e) {
            }
        }
        return defaultString;
    }
    
    /**
     * 获取Boolean类型的值
     * 
     * <pre>
     *   getValueBoolean(null)    = null
     *   getValueBoolean("true")  = Boolean.TRUE
     *   getValueBoolean("false") = Boolean.FALSE
     *   getValueBoolean("on")    = Boolean.TRUE
     *   getValueBoolean("ON")    = Boolean.TRUE
     *   getValueBoolean("yEs")   = Boolean.TRUE
     *   getValueBoolean("off")   = Boolean.FALSE
     *   getValueBoolean("oFf")   = Boolean.FALSE
     *   getValueBoolean("1")     = Boolean.TRUE
     *   getValueBoolean("0")     = Boolean.FALSE
     *   getValueBoolean("blue")  = null
     * </pre>
     * 
     * @param key 键
     * 
     * @return Boolean 值,可能为null
     * @exception [异常类型] [异常说明]
     * @see org.apache.commons.lang3.BooleanUtils#toBooleanObject(String)
     */
    public static Boolean getValueBoolean(String key) {
        String value = getValue(key);
        // 优先考虑value最可能出现的情况:字符串"true"和"false"
        if (StringUtils.equalsIgnoreCase("true", value)) {
            return Boolean.TRUE;
        } else if (StringUtils.equalsIgnoreCase("false", value)) {
            return Boolean.FALSE;
        } else
        // BooleanUtils.toBooleanObject(String) 方法没有考虑到 "0" 和 "1" 的问题.
        if (StringUtils.equals("1", value)) {
            return Boolean.TRUE;
        } else if (StringUtils.equals("0", value)) {
            return Boolean.FALSE;
        } else {
            return BooleanUtils.toBooleanObject(value);
        }
    }
    
    /**
     * 获取boolean类型的值
     * 
     * <pre>
     *   getValueBoolean(null)    = defaultBoolean
     *   getValueBoolean("true")  = Boolean.TRUE
     *   getValueBoolean("false") = Boolean.FALSE
     *   getValueBoolean("on")    = Boolean.TRUE
     *   getValueBoolean("ON")    = Boolean.TRUE
     *   getValueBoolean("yEs")   = Boolean.TRUE
     *   getValueBoolean("off")   = Boolean.FALSE
     *   getValueBoolean("oFf")   = Boolean.FALSE
     *   getValueBoolean("1")     = Boolean.TRUE
     *   getValueBoolean("0")     = Boolean.FALSE
     *   getValueBoolean("blue")  = defaultBoolean
     * </pre>
     * 
     * @param key 键
     * @param defaultBoolean 默认值
     * 
     * @return boolean 值
     * @exception [异常类型] [异常说明]
     * @see org.apache.commons.lang3.BooleanUtils#toBooleanObject(String)
     */
    public static boolean getValueBoolean(String key, boolean defaultBoolean) {
        boolean contains = getContext().contains(key);
        if (contains) {
            try {
                return getValueBoolean(key).booleanValue();
            } catch (Exception e) {
            }
        }
        return defaultBoolean;
    }
    
    /**
     * 
     * 获取日期类型的值<br>
     * 尽最大可能的从上至下进行日期的尝试解析
     * 
     * @param key 建
     * 
     * @return Date [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see RainhyConstant#COMMON_DATE_PATTERN
     * @version [版本号, 2016年8月10日]
     * @author rain
     */
    public static Date getValueDate(String key) {
        String value = getValue(key);
        try {
            return DateUtils.parseDate(value, RainhyConstant.COMMON_DATE_PATTERN);
        } catch (ParseException e) {
            throw new RainhyParseOrFormatException("日期格式解析错误: {} => {}", value, StringUtils.join(RainhyConstant.COMMON_DATE_PATTERN));
        }
    }
    
    /**
     * 
     * 获取日期类型的值<br>
     * 尽最大可能的从上至下进行日期的尝试解析
     *
     * @param key 建
     * @param defaultDate 默认值
     * 
     * @return Date [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see RainhyConstant#COMMON_DATE_PATTERN
     * @version [版本号, 2016年8月10日]
     * @author rain
     */
    public static Date getValueDate(String key, Date defaultDate) {
        boolean contains = getContext().contains(key);
        if (contains) {
            try {
                return getValueDate(key);
            } catch (Exception e) {
            }
        }
        return defaultDate;
    }
    
    /**
     * 获取Integer类型的值
     * 
     * @param key 键
     * 
     * @return Integer 值,可能为null
     * @exception [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    public static Integer getValueInteger(String key) {
        String value = getValue(key);
        try {
            return Integer.valueOf(value);
        } catch (NumberFormatException e) {
            throw new RainhyParseOrFormatException("数字格式错误: {}", value);
        }
    }
    
    /**
     * 获取int类型的值
     * 
     * @param key 键
     * @param defaultLong 默认值
     * 
     * @return int 值
     * @exception [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    public static int getValueInteger(String key, int defaultInt) {
        boolean contains = getContext().contains(key);
        if (contains) {
            try {
                return getValueInteger(key).intValue();
            } catch (Exception e) {
            }
        }
        return defaultInt;
    }
    
    /**
     * 
     * 获取指定类型的值<br>
     * 在配置中存入 json 字符串
     *
     * @param key 键
     * @param clazz 值类型
     * 
     * @return T 值
     * @exception [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     * @version [版本号, 2016年9月20日]
     * @author rain
     */
    public static <T> T getValueJson(String key, Class<T> clazz) {
        String value = getValue(key);
        try {
            return JsonUtils.fromJson(value, clazz);
        } catch (Exception e) {
            throw new RainhyParseOrFormatException("Json格式错误: {}", value);
        }
    }
    
    /**
     * 
     * 获取指定类型的值<br>
     * 在配置中存入 json 字符串
     *
     * @param key 键
     * @param defaultT 默认值
     * @param clazz 值类型
     * 
     * @return T 值
     * @exception [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     * @version [版本号, 2016年9月20日]
     * @author rain
     */
    public static <T> T getValueJson(String key, T defaultT, Class<T> clazz) {
        boolean contains = getContext().contains(key);
        if (contains) {
            try {
                return getValueJson(key, clazz);
            } catch (Exception e) {
            }
        }
        return defaultT;
    }
    
    /**
     * 
     * 获取指定类型的值(列表)<br>
     * 在配置中存入 json 字符串
     *
     * @param key 键
     * @param clazz 值类型
     * 
     * @return T 值
     * @exception [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     * @version [版本号, 2016年9月20日]
     * @author rain
     */
    public static <T> List<T> getValueJsonList(String key, Class<T> clazz) {
        String value = getValue(key);
        try {
            return JsonUtils.fromJsonForRootIsList(value, clazz);
        } catch (Exception e) {
            throw new RainhyParseOrFormatException("Json格式错误: {}", value);
        }
    }
    
    /**
     * 
     * 获取指定类型的值(列表)<br>
     * 在配置中存入 json 字符串
     *
     * @param key 键
     * @param defaultT 默认值
     * @param clazz 值类型
     * 
     * @return T 值
     * @exception [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     * @version [版本号, 2016年9月20日]
     * @author rain
     */
    public static <T> List<T> getValueJsonList(String key, List<T> defaultT, Class<T> clazz) {
        boolean contains = getContext().contains(key);
        if (contains) {
            try {
                return getValueJsonList(key, clazz);
            } catch (Exception e) {
            }
        }
        return defaultT;
    }
    
    /**
     * 获取Long类型的值
     * 
     * @param key 键
     * 
     * @return Long 值,可能为null
     * @exception [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    public static Long getValueLong(String key) {
        String value = getValue(key);
        try {
            return Long.valueOf(value);
        } catch (NumberFormatException e) {
            throw new RainhyParseOrFormatException("数字格式错误: {}", value);
        }
    }
    
    /**
     * 获取long类型的值
     * 
     * @param key 键
     * @param defaultLong 默认值
     * 
     * @return long 值
     * @exception [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    public static long getValueLong(String key, long defaultLong) {
        boolean contains = getContext().contains(key);
        if (contains) {
            try {
                return getValueLong(key).longValue();
            } catch (Exception e) {
            }
        }
        return defaultLong;
    }
    
    /** 配置属性映射(key:key) */
    private Map<String, ConfigProperty> configPropertyMapping = new HashMap<String, ConfigProperty>();
    
    /** 配置属性组映射(key:name) */
    private Map<String, ConfigGroup> configGroupMapping = new HashMap<String, ConfigGroup>();
    
    /** 添加配置属性到配置容器 */
    public void addConfigProperty(ConfigProperty configProperty) {
        AssertUtils.notNull(configProperty, "配置属性为 null");
        String configPropertyKey = configProperty.getKey();
        AssertUtils.notEmpty(configPropertyKey, "配置属性.key不能为空!");
        AssertUtils.matcher(configPropertyKey, RegExp.单词点, "配置属性.key只能包含[{}]", "大小写字母数字下划线点");
        AssertUtils.notContains(this.configPropertyMapping, configPropertyKey, "配置属性重复.key: {}", configPropertyKey);    //如果设置为不可以重复，则校验是否已经存在相同key的配置属性
        this.configPropertyMapping.put(configPropertyKey, configProperty);
    }
    
    /** 添加配置属性组到配置容器 */
    public void addConfigPropertyGroup(ConfigGroup configGroup) {
        AssertUtils.notNull(configGroup, "配置组为 null");
        String configGroupName = configGroup.getName();
        AssertUtils.notEmpty(configGroupName, "配置组.name不能为空!");
        AssertUtils.matcher(configGroupName, RegExp.单词点中文, "配置属性.key只能包含[{}]", "大小写字母数字下划线点中文");
        AssertUtils.notContains(this.configGroupMapping, configGroupName, "配置组重复.name: {}", configGroupName);
        this.configGroupMapping.put(configGroupName, configGroup);
    }
    
    @Override
    public void afterPropertiesSet() throws Exception {
        ConfigContext.context = this;
        super.afterPropertiesSet();
        
        ///// 加载配置
        if (MapUtils.isNotEmpty(this.configManagerMapping)) {
            for (ConfigManager configManager : this.configManagerMapping.values()) {
                configManager.loadConfig(this);
            }
        }
        
        ///// 打印已经加载的配置
        if (MapUtils.isNotEmpty(this.configPropertyMapping)) {
            for (Map.Entry<String, ConfigProperty> entry : this.configPropertyMapping.entrySet()) {
                String key = entry.getKey();
                ConfigProperty value = entry.getValue();
                logger.info("     加载配置项: {} = {}", key, value.getValue());
            }
        }
        
        logger.debug("[配置容器]: 启动完成...");
    }
    
    /** 判断是否存在此 key 对应的值 */
    public boolean contains(String key) {
        return this.configPropertyMapping.containsKey(key);
    }
    
    /** 配置属性分组(key:name) */
    @SuppressWarnings("unchecked")
    public Map<String, ConfigGroup> getAllConfigPropertyGroupMap() {
        return MapUtils.unmodifiableMap(this.configGroupMapping);
    }
    
    /** 获取所有的配置属性(key:key) */
    @SuppressWarnings("unchecked")
    public Map<String, ConfigProperty> getAllConfigPropertyMap() {
        return MapUtils.unmodifiableMap(this.configPropertyMapping);
    }
    
    /** 根据key获取对应的配置属性实例(如果 key 对应的配置属性不存在,则抛错) */
    public ConfigProperty getConfigProperty(String key) {
        AssertUtils.contains(this.configPropertyMapping, key, "跟[key:{}]对应的配置属性不存在.", key);
        return this.configPropertyMapping.get(key);
    }
    
    /** 根据key获取对应的配置属性的实际值(如果 key 对应的配置属性不存在,则抛错) */
    public String getConfigPropertyValue(String key) {
        return getConfigProperty(key).getValue();
    }
    
    /** 更新属性值 */
    public void update(String key, String value) {
        AssertUtils.contains(this.configPropertyMapping, key, "跟[key:{}]对应的配置属性不存在.", key);
        getConfigProperty(key).update(value);
    }
}
